//========================================================================
/**
 * Author   : cuisw <shaovie@gmail.com>
 * Date     : 2008-11-10 22:37
 */
//========================================================================

#ifndef _MEMORYPOOL_H_
#define _MEMORYPOOL_H_
#include "Pre.h"

#include "ThreadMutex.h"

#include <stdio.h>
#include <malloc.h>

/**
 * @class ObjectHeader 
 *
 * @brief This information is stored in memory allocated by the memory pool. 
 */
/*************************************************************************
 *
 *          +------------> This is a slab, size = 1M, 
 *          |
 * +---------------------+   
 * |               	     |
 * |    SlabHeader:0     |        --> Slabs are chained to double-link.
 * |                     |
 * +---------------------+
 *          |
 *	       \|/
 * +---------------------+
 * | ObjectHeader:inuse  |<---+
 * |------------+--------+    |
 * |            |  prev  |--> 0
 * +------------+--------+    |
 * |		    |  next  |--+ |  
 * |=====================+  | |
 * |			         |  | |
 * |			         |  | |
 * |	        		 |  | |
 * | Useable Memory 	 |  | |   --> Application acquire this object, size = 2K.
 * |        			 |  | |
 * |		        	 |  | |
 * |        			 |  | |
 * +---------------------+  | |
 * | ObjectHeader:free	 |<-+-++
 * +------------+--------+  | ||
 * |            |  prev  |--+-++
 * +------------+--------+     |
 * |    		|  next  |--+  |  
 * |=====================+  |  |
 * |	        		 |  |  |
 * |	        		 |  |  |
 * | Useable Memory      |  |  |  --> Application release this object, size = 1.5K.
 * |	        		 |  |  |
 * |	        		 |  |  |
 * +---------------------+  |  |
 * | ObjectHeader:inuse  |<-+-+|
 * +------------+--------+    ||
 * |            |  prev  |----++
 * +------------+--------+    |
 * |	    	|  next  |--+ | 
 * |=====================+  | |
 * |		        	 |  | |
 * |    	    		 |  | |
 * |	        		 |  | |
 * |	        		 |  | |
 * |		        	 |  | |
 * |	        		 |  | |
 * | Useable Memory      |  | |   --> Application acquire this object, size = 3K.
 * |	        		 |  | |
 * |	        		 |  | |
 * |		        	 |  | |
 * |		        	 |  | |
 * |        			 |  | |
 * |		        	 |  | |
 * +---------------------+  | |
 * | ObjectHeader:free   |<-+-+   --> Seriate free memory,
 * +------------+--------+    |
 * |	      	|  prev  |----+
 * +------------+--------+  
 * |	    	|  next  |--> 0 
 * |=====================+   
 * |		        	 |   
 * |		        	 |   
 * |	        		 |        
 *          ....
 *          ....
 * |	        		 |  
 * |	        		 |  
 * +---------------------+  
 *
 */

union max_align_info
{
    int (*i)();
    void *p;
    long l;
    double d;
};

#define MALLOC_ALIGN (sizeof (max_align_info))
#define MALLOC_ROUNDUP(X, Y) ((X) + ((Y) - 1) & ~((Y) - 1)) 

#if defined(DUMP_INFO)
#   undef MALLOC_ALIGN
#endif

/**
 * @class MemoryPool
 *
 * @brief This is a memory pool based slabs technic.
 */
class MemoryPool
{
public:
    //
    virtual ~MemoryPool ();

    //
    MemoryPool ();

    /**
	 *
     * <slab_size>			: The size of slab be alloced one time.
	 * 						  Note: <slab_size> must greater than size 
	 * 						  of(ObjectHeader).
	 *
     * <prealloc_slab_num> 	: The number of slabs prealloc when mem-pool init.
	 *
	 * <max_slabs>			: The max size of this memory pool apply from OS.
	 * 						  If the value is 0 or -1, then the max size is unlimited.
     */
	int init (int slab_size = 1024 * 1024/*1M*/, 
			int prealloc_slab_num = 128,
			int max_slabs = 1024/*1G*/);

    void *malloc (size_t size);

    void *alloc (size_t size);

    void free (void *);

	void dump (void);
protected:
    /**
     * @class ObjectHeader 
     *
     * @brief This object be stored in every memory object that is alloced 
     * by application, store the slab id , object size and so on.
     */
    class SlabHeader;
    class ObjectHeader
    {
    public:
		ObjectHeader ();

	public:
		int inuse_;

		// Size of the memory pass to application(not include 
		// sizeof(ObjectHeader) 
		size_t size_;

		// The slab which this memory object places.
		SlabHeader *slab_;

		// Prev object.
		ObjectHeader *prev_;

		// Next object.
		ObjectHeader *next_;

#if defined(MALLOC_ALIGN)
        // For align
        char align[MALLOC_ROUNDUP(sizeof (int) + sizeof (size_t) + \
                sizeof (SlabHeader *) + sizeof (ObjectHeader *) * 2, \
                MALLOC_ALIGN) \
            - (sizeof (int) + sizeof (size_t) + sizeof (SlabHeader *) \
                    + sizeof (ObjectHeader *) * 2)];
#endif
    };
    /**
     * @class SlabHeader
     *
     * @brief This object be stored in every slab. store the memory header
     * pointer, size ,the scale of being used and so on.
     */
    class SlabHeader
    {
    public:
		SlabHeader (size_t slab_size);
    public:
		// Num of objects active in slab
		size_t inuse_;

#ifdef DUMP_INFO
		// The size of free memory actually in this slab(maybe not seriate).
		size_t usable_size_;
#endif
		// Max size of seriate object in this slab.
		size_t max_seriate_size_;

		ObjectHeader *head_;

		ObjectHeader *max_seriate_free_head_;

		// =
		SlabHeader *prev_;

		SlabHeader *next_;
    };

    void init_object (void *, SlabHeader *slab, size_t size);

	SlabHeader *alloc_slabs (size_t count);

	SlabHeader *find_usable_slab (size_t size);

	void ajust_slab_order (SlabHeader *slab, size_t alloc_size);

	ObjectHeader *pop_usable_object (SlabHeader *slab, size_t alloc_size);
private:
    // = Config
    size_t slab_size_;

    size_t prealloc_slab_num_;

    size_t max_slabs_;

    // Current alloced slabs
    size_t alloced_slabs_;

    // = List of slabs.
    SlabHeader *slabs_head_;

    SlabHeader *slabs_tail_;

	// Slabs mutex.
	ThreadMutex mutex_;
};

#include "Post.h"
#endif

